feat(hooks): wire mcp_tool + agent hook dispatchers in the agent loop#114
Merged
Conversation
The HookDispatcher already had mcp_tool + agent handler cases, but nothing
supplied their dispatchers, so those hook types no-op'd with "no dispatcher
wired". The host CLI can't supply them at HookDispatcher construction time:
mcp_tool needs the live tool registry (MCP tools are registered as
`mcp__<server>__<tool>`) and `agent` needs the sub-agent runner — both only
exist once the agent loop starts. So the loop wires them itself.
- HookDispatcher: mcpToolDispatcher/agentDispatcher are now settable via
setMcpToolDispatcher()/setAgentDispatcher() (+ hasX() probes). Setters
no-op if a dispatcher was already supplied, so explicit/plugin wiring wins.
- agent.ts: after building runSubAgent, wires:
· mcp_tool → resolves `mcp__<server>__<tool>` from the registry and runs it
in the live ToolContext (maps isError → exitCode).
· agent → runs the named sub-agent via runSubAgent, with a re-entrancy guard
(the dispatcher is shared with the sub-agent loop, so a hook firing during
the sub-agent run is skipped rather than cascading). Only wired below the
sub-agent recursion cap.
Tests: +4 dispatcher (set/has, late-wired invocation for both types,
constructor-wins) +2 agent (mcp_tool hook runs a registered MCP tool
end-to-end through the loop; unregistered tool reported without throwing).
Core suite 564 green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Activates two hook handler types that were implemented but inert. The
HookDispatcheralready hadmcp_toolandagentcases, but nothing supplied their dispatchers — so configuring those hooks just produced"no mcpToolDispatcher wired"/"no agentDispatcher wired"on stderr.They can't be wired at the host's
HookDispatcherconstruction site (the "ordering" problem):mcp_toolneeds the live tool registry (MCP tools are registered asmcp__<server>__<tool>) andagentneeds the sub-agent runner — both only exist once the agent loop has started. So the agent loop wires them itself, which is the correct layer and needs no changes torepl.ts/headless.ts.Changes
hooks/dispatcher.ts—mcpToolDispatcher/agentDispatcherbecome settable:setMcpToolDispatcher(fn)/setAgentDispatcher(fn)+hasMcpToolDispatcher()/hasAgentDispatcher()probes.agent.ts— after buildingrunSubAgent, the loop wires:mcp__<server>__<tool>fromopts.toolsand executes it in the liveToolContext(isError→ non-zero exit code; missing tool → descriptive stderr, no throw).runSubAgent, guarded by:Tests
dispatcher.test.ts):set/hasround-trip, late-wired invocation for bothmcp_toolandagent, and constructor-wins-over-setter.agent.test.ts): aPostToolUsemcp_toolhook resolves + runs a registeredmcp__test__echotool end-to-end through the real agent loop (asserts both dispatchers got wired and the tool ran exactly once); an unregistered MCP tool is reported without throwing.Core suite 564 green (+6); full repo suite green via pre-commit.
Notes
🤖 Generated with Claude Code